#include "doge_mempool.h"

// #define DOGE_USE_LOG


static char memptr[DOGE_MEM_SIZE] __attribute__ ((aligned (DOGE_MEM_BLOCK_SIZE)));    //__attribute__((at(0XC0600000)))  小端

#define MEM_ADDR_START  &memptr[0]
#define MEM_ADDR_END  &memptr[DOGE_MEM_SIZE-1]

struct mem_struct {
    void *ptr;
    unsigned int size;
    unsigned int block_size;
    unsigned int used[DOGE_MEM_SIZE/DOGE_MEM_BLOCK_SIZE];
};

static char init_ok = 0;       //__attribute__((at(0XC0600000)))

struct mem_struct mem_obj;

static unsigned int free_block;
static void *free_ptr = NULL;

static void doge_malloc_init(void)
{
    unsigned int index;

    if (!init_ok)
    {
        init_ok = 1;
        mem_obj.ptr = MEM_ADDR_START;
        mem_obj.block_size = DOGE_MEM_BLOCK_SIZE;
        mem_obj.size = DOGE_MEM_SIZE;
        for (index = 0; index < (DOGE_MEM_SIZE/DOGE_MEM_BLOCK_SIZE); index++)
        {
            mem_obj.used[index] = 0;
        }
    #ifdef DOGE_USE_LOG
        printf("[%s]-%d MEM_ADDR_START %p, MEM_ADDR_END %p\r\n", __FUNCTION__ , __LINE__, MEM_ADDR_START, MEM_ADDR_END);
    #endif
    }
}

int doge_mp_get_size(void)
{
    doge_malloc_init();

    return mem_obj.size;
}

void *doge_mp_malloc(unsigned int size)
{
    signed long index;
    int i;
    int need_blocks;   /* number of blocks to need */   
    int not_used_blocks = 0;

    doge_malloc_init();

    if (size == 0)
        return NULL;

    /* Calculate the number of memory blocks required to apply for the memory size */
    need_blocks = size/DOGE_MEM_BLOCK_SIZE;
    if (size % DOGE_MEM_BLOCK_SIZE)     
        need_blocks++;

#ifdef DOGE_USE_LOG
    printf("[%s]-%d, need blocks %d\r\n", __FUNCTION__, __LINE__, need_blocks);
#endif

    for ( index = (DOGE_MEM_SIZE/DOGE_MEM_BLOCK_SIZE)-1; index >= 0; index--)
    {
        if (mem_obj.used[index] == 0)       /* the memory block is not used */ 
        {
            not_used_blocks++;                 /* Count the number of contiguous unused memory blocks */ 
        }
        else
        {
            not_used_blocks = 0;
        }
        /* The contiguous memory blocks match the size of the requested memory 如果连续内存块符合申请大小 */ 
        if (not_used_blocks == need_blocks)     
        {
            for (i = 0; i < need_blocks; i++)
            {
                mem_obj.used[index+i] = need_blocks;
            }
            printf("malloc index %d\r\n", index);
            return (void *)(mem_obj.ptr+index*DOGE_MEM_BLOCK_SIZE);
        }

    }
#ifdef DOGE_USE_LOG
    printf("[%s]-%d, malloc failed\r\n", __FUNCTION__, __LINE__);
#endif
    return NULL;
}

int doge_mp_free(void *ptr)
{
    int index;
    int used_blocks;   /* count of used block */
    int offset;   
    int i;

    if (ptr == NULL)
    {
    #ifdef DOGE_USE_LOG
        printf("[%s]-%d : ptr is null\r\n", __FUNCTION__, __LINE__);
    #endif
        return -1;
    }

    offset = (char *)ptr - MEM_ADDR_START;  
#ifdef DOGE_USE_LOG
    printf("[%s]-%d : free addr %p, offset %d\r\n", __FUNCTION__, __LINE__, ptr, offset);
#endif
    if (offset > DOGE_MEM_SIZE)    /* This memory is not belong to this memory pool */ 
    {
#ifdef DOGE_USE_LOG
        printf("[%s]-%d : offset is out of range\r\n",  __FUNCTION__, __LINE__);
#endif
        return -2;
    }

    doge_malloc_init();

    index = offset / DOGE_MEM_BLOCK_SIZE;  /* Get index of memory blocks */
    used_blocks = mem_obj.used[index];     /* Get count of used block */  
    printf("free index %d\r\n", index);
#ifdef DOGE_USE_LOG
    printf("[%s]-%d : index %d, used_blocks %d\r\n", __FUNCTION__, __LINE__, index, used_blocks);
#endif
    /* Clear used flag, indicates that the memory is released */
    for (i = 0; i < used_blocks; i++)
    {
        mem_obj.used[index+i] = 0;     
    }
    free_block = used_blocks;
    free_ptr = ptr;
    ptr = NULL;
    return 0;
}

static int doge_mp_get_memory_index(void *ptr)
{
    int index = 0;
    int offset;   

    if (ptr == NULL) return -1;

    offset = (char *)ptr - MEM_ADDR_START;
    if (offset > DOGE_MEM_SIZE)    /* This memory is not belong to this memory pool */ 
    {
#ifdef DOGE_USE_LOG
        printf("[%s]-%d : offset is out of range\r\n",  __FUNCTION__, __LINE__);
#endif
        return -2;
    }  

    index = offset / DOGE_MEM_BLOCK_SIZE;  /* Get index of memory blocks */
    return index;
}

static int doge_mp_get_memory_size(void *ptr)
{
    int offset;   
    int index;
    int used_blocks;   /* count of used block */

    index = doge_mp_get_memory_index(ptr);
    if (index < 0) return -1;

    used_blocks = mem_obj.used[index];     /* Get count of used block */  
    return (used_blocks * DOGE_MEM_BLOCK_SIZE);
}

static int doge_get_used_blocks_by_index(int index)
{
    return mem_obj.used[index];
}

static void doge_memcpy(void *dest, void *src, int size)
{
    char *d = (char *) dest;
    char *s = (char *) src;

    while (size--)
    {
        *d++ = *s++;
    }
}

#if 1
void *doge_mp_realloc(void *ptr, unsigned int size)
{
    if (ptr == NULL) 
    {
        ptr = doge_mp_malloc(size);
        return ptr;
    }
      
    if (size == 0)
    {
        doge_mp_free(ptr);
        return NULL;
    }
      
    int old_size = doge_mp_get_memory_size(ptr);
    if (old_size == size) 
        return ptr;   

    char *newptr = (char *)doge_mp_malloc(size);
    if (newptr == NULL)                
        return NULL;

    if (old_size > size)
    {
        doge_memcpy(newptr, ptr, size);
    }
    else   
    {
        doge_memcpy(newptr, ptr, old_size);
    }

    doge_mp_free(ptr);       
    return (void *)newptr;
}
#else
void *doge_mp_realloc(void *ptr, unsigned int size)
{
    int dt;

    if (ptr == NULL) 
    {
        ptr = doge_mp_malloc(size);
        return ptr;
    }
      
    if (size == 0)
    {
        doge_mp_free(ptr);
        return NULL;
    }
      
    int old_size = doge_mp_get_memory_size(ptr);
    if (old_size == size) 
        return ptr;   

    char *newptr = (char *)doge_mp_malloc(size);
    if (newptr == NULL)                
        return NULL;

    int blks;
    int index;
    int used_blks;
    if (old_size > size)        /* Memory to reduce */
    {
        dt = old_size - size;
        blks = dt / DOGE_MEM_BLOCK_SIZE;
        if (blks % DOGE_MEM_BLOCK_SIZE) blks++;
        index = doge_mp_get_memory_index(ptr);
        used_blks = doge_get_used_blocks_by_index(index);

        for (int i = index+(used_blks-blks); i < (index+used_blks); i++)
        {
            mem_obj.used[i] = 0;     
        }
    }
    else if (old_size < size)
    {

    }

    if (old_size > size)
    {
        doge_memcpy(newptr, ptr, size);
    }
    else   
    {
        doge_memcpy(newptr, ptr, old_size);
    }

    doge_mp_free(ptr);       
    return (void *)newptr;
}
#endif
